/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Supplier;

import org.aopalliance.aop.Advice;

import org.springframework.aop.Pointcut;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.function.SingletonSupplier;

/**
 * Advisor that activates asynchronous method execution through the {@link Async}
 * annotation. This annotation can be used at the method and type level in
 * implementation classes as well as in service interfaces.
 *
 * <p>This advisor detects the EJB 3.1 {@code javax.ejb.Asynchronous}
 * annotation as well, treating it exactly like Spring's own {@code Async}.
 * Furthermore, a custom async annotation type may get specified through the
 * {@link #setAsyncAnnotationType "asyncAnnotationType"} property.
 *
 * @author Juergen Hoeller
 * @since 3.0
 * @see Async
 * @see AnnotationAsyncExecutionInterceptor
 */
/*
1. 总结
AsyncAnnotationAdvisor 是 Spring 框架中用于处理 @Async 注解的 AOP 顾问类，负责识别和增强标记了异步执行注解的方法
该类继承自 AbstractPointcutAdvisor 并实现 BeanFactoryAware 接口，具备完整的 AOP 顾问功能和 Bean 工厂感知能力
支持 Spring 自身的 @Async 注解以及 EJB 3.1 的 @javax.ejb.Asynchronous 注解，并允许自定义异步注解类型
2. 内部成员说明
字段
private Advice advice: 存储异步执行的增强逻辑，实际类型为 AnnotationAsyncExecutionInterceptor
private Pointcut pointcut: 存储用于匹配异步注解的切入点
构造方法
public AsyncAnnotationAdvisor(): 默认构造方法，创建基本的异步注解顾问
public AsyncAnnotationAdvisor(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler): 带执行器和异常处理器的构造方法
public AsyncAnnotationAdvisor(Supplier<Executor> executor, Supplier<AsyncUncaughtExceptionHandler> exceptionHandler): 带执行器和异常处理器供应者的构造方法
主要方法
public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType): 设置自定义的异步注解类型
public void setBeanFactory(BeanFactory beanFactory): 实现 BeanFactoryAware 接口，设置 Bean 工厂
public Advice getAdvice(): 获取增强逻辑
public Pointcut getPointcut(): 获取切入点
protected Advice buildAdvice(Supplier<Executor> executor, Supplier<AsyncUncaughtExceptionHandler> exceptionHandler): 构建异步执行拦截器
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes): 构建匹配异步注解的切入点
3. 类和接口关系
Advisor		AOP 切面的基本抽象，是最顶层接口，定义了最基本的方法 getAdvice() 和 isPerInstance()
└── PointcutAdvisor	扩展 Advisor，添加切点概念，增加了 getPointcut() 方法，用于确定通知的应用位置，用于常规的方法拦截场景
    └── AbstractPointcutAdvisor	PointcutAdvisor的抽象基类实现
 	     └── AsyncAnnotationAdvisor	处理 @Async 注解，负责识别和增强标记了异步执行注解的方法
相关类和接口区别说明
	AsyncAnnotationAdvisor: 专门处理 @Async 注解的顾问类，自动识别类级别和方法级别的异步注解
	AbstractPointcutAdvisor: 抽象的切入点顾问基类，提供基本的顾问实现框架
	AnnotationAsyncExecutionInterceptor: 实际执行异步方法调用的拦截器，是 AsyncAnnotationAdvisor 的核心组件
	Async: Spring 提供的异步执行注解
	AnnotationMatchingPointcut: 用于匹配注解的切入点实现类
4. 使用场景
	在 Spring 应用中启用基于注解的异步方法执行功能
	将普通同步方法透明地转换为异步执行，提高应用性能
	支持自定义异步注解类型，满足特殊业务需求
	与 Spring 的任务执行框架集成，提供完整的异步处理解决方案
5. 实际代码应用示例
// 服务类使用 @Async 注解
@Service
public class UserService {

    @Async
    public CompletableFuture<String> findUserAsync(String userId) {
        // 模拟耗时操作
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return CompletableFuture.completedFuture("User: " + userId);
    }

    @Async
    public void sendEmailAsync(String email) {
        // 发送邮件的异步操作
        System.out.println("Sending email to: " + email);
    }
}

// 配置类启用异步支持
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    public AsyncAnnotationAdvisor asyncAnnotationAdvisor() {
        return new AsyncAnnotationAdvisor();
    }

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }
}

// 使用示例
@Component
public class UserController {

    @Autowired
    private UserService userService;

    public void handleUserRequest(String userId) {
        // 异步调用，不会阻塞当前线程
        CompletableFuture<String> userFuture = userService.findUserAsync(userId);
        userService.sendEmailAsync("user@example.com");

        // 可以继续处理其他逻辑
        userFuture.thenAccept(user -> {
            System.out.println("Received user: " + user);
        });
    }
}

 */
@SuppressWarnings("serial")
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {

	private Advice advice;

	private Pointcut pointcut;


	/**
	 * Create a new {@code AsyncAnnotationAdvisor} for bean-style configuration.
	 */
	public AsyncAnnotationAdvisor() {
		this((Supplier<Executor>) null, (Supplier<AsyncUncaughtExceptionHandler>) null);
	}

	/**
	 * Create a new {@code AsyncAnnotationAdvisor} for the given task executor.
	 * @param executor the task executor to use for asynchronous methods
	 * (can be {@code null} to trigger default executor resolution)
	 * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to
	 * handle unexpected exception thrown by asynchronous method executions
	 * @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
	 */
	@SuppressWarnings("unchecked")
	public AsyncAnnotationAdvisor(
			@Nullable Executor executor, @Nullable AsyncUncaughtExceptionHandler exceptionHandler) {

		this(SingletonSupplier.ofNullable(executor), SingletonSupplier.ofNullable(exceptionHandler));
	}

	/**
	 * Create a new {@code AsyncAnnotationAdvisor} for the given task executor.
	 * @param executor the task executor to use for asynchronous methods
	 * (can be {@code null} to trigger default executor resolution)
	 * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to
	 * handle unexpected exception thrown by asynchronous method executions
	 * @since 5.1
	 * @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
	 */
	@SuppressWarnings("unchecked")
	public AsyncAnnotationAdvisor(
			@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {

		Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
		asyncAnnotationTypes.add(Async.class);
		try {
			asyncAnnotationTypes.add((Class<? extends Annotation>)
					ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
		}
		catch (ClassNotFoundException ex) {
			// If EJB 3.1 API not present, simply ignore.
		}
		this.advice = buildAdvice(executor, exceptionHandler);
		this.pointcut = buildPointcut(asyncAnnotationTypes);
	}


	/**
	 * Set the 'async' annotation type.
	 * <p>The default async annotation type is the {@link Async} annotation, as well
	 * as the EJB 3.1 {@code javax.ejb.Asynchronous} annotation (if present).
	 * <p>This setter property exists so that developers can provide their own
	 * (non-Spring-specific) annotation type to indicate that a method is to
	 * be executed asynchronously.
	 * @param asyncAnnotationType the desired annotation type
	 */
	public void setAsyncAnnotationType(Class<? extends Annotation> asyncAnnotationType) {
		Assert.notNull(asyncAnnotationType, "'asyncAnnotationType' must not be null");
		Set<Class<? extends Annotation>> asyncAnnotationTypes = new HashSet<>();
		asyncAnnotationTypes.add(asyncAnnotationType);
		this.pointcut = buildPointcut(asyncAnnotationTypes);
	}

	/**
	 * Set the {@code BeanFactory} to be used when looking up executors by qualifier.
	 */
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		if (this.advice instanceof BeanFactoryAware) {
			((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
		}
	}


	@Override
	public Advice getAdvice() {
		return this.advice;
	}

	@Override
	public Pointcut getPointcut() {
		return this.pointcut;
	}


	protected Advice buildAdvice(
			@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {

		AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
		interceptor.configure(executor, exceptionHandler);
		return interceptor;
	}

	/**
	 * Calculate a pointcut for the given async annotation types, if any.
	 * @param asyncAnnotationTypes the async annotation types to introspect
	 * @return the applicable Pointcut object, or {@code null} if none
	 */
	protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
		ComposablePointcut result = null;
		for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
			Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
			Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
			if (result == null) {
				result = new ComposablePointcut(cpc);
			}
			else {
				result.union(cpc);
			}
			result = result.union(mpc);
		}
		return (result != null ? result : Pointcut.TRUE);
	}

}
